package org.nimi317.web_gis.data;

import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.baomidou.mybatisplus.core.batch.MybatisBatch;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.CollectionType;
import jakarta.annotation.PostConstruct;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.ibatis.exceptions.PersistenceException;
import org.apache.ibatis.session.SqlSessionFactory;
import org.nimi317.web_gis.context.SecurityContextHolder;
import org.nimi317.web_gis.dao.AreaQualityMapper;
import org.nimi317.web_gis.dao.ProvinceQualityDao;
import org.nimi317.web_gis.dao.StationMapper;
import org.nimi317.web_gis.dao.StationQualityMapper;
import org.nimi317.web_gis.division.DivisionInterface;
import org.nimi317.web_gis.entity.*;
import org.nimi317.web_gis.exception.JsonException;
import org.nimi317.web_gis.exception.StationQualityExistException;
import org.nimi317.web_gis.factory.BaseDivisionFactory;
import org.nimi317.web_gis.form.get.StationQualityWeb;
import org.nimi317.web_gis.service.AreaService;
import org.nimi317.web_gis.service.ICurrentService;
import org.nimi317.web_gis.utils.QualityUtils;
import org.nimi317.web_gis.utils.StringUtils;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;

/**
 * @author thunderobot
 */
@Component
@Slf4j
public class AreaCodeList {

    /**
     * 注入AreaService
     */
    private final AreaService areaService;

    /**
     * jackson解析
     */
    private final ObjectMapper objectMapper;


    /**
     * 解析list
     */
    private final CollectionType type;

    /**
     * mybatisSession
     */
    private final SqlSessionFactory sqlSessionFactory;

    /**
     * 站点mapper对象
     */
    private final StationMapper stationMapper;

    /**
     * 站点数据mapper
     */
    private final StationQualityMapper stationQualityMapper;

    /**
     * 策略
     */
    private static final Map<String, DivisionInterface> strategy = new HashMap<>();

    /**
     * 策略的单例对象
     */
    private final Map<String, BaseDivisionFactory> factory = new HashMap<>();

    /**
     * 城区数据
     */
    private List<Area> areaCodes;

    private final AreaQualityMapper areaQualityMapper;

    private String tableName;

    public static final Set<String> abnormalArray = new HashSet<>(List.of("0", "null", "-", "—"));

    public static final String abnormal = "0";

    private final ProvinceQualityDao provinceMapper;

    public AreaCodeList(AreaService areaService, ObjectMapper objectMapper, SqlSessionFactory sqlSessionFactory, StationMapper stationMapper, StationQualityMapper stationQualityMapper, AreaQualityMapper areaQualityMapper, ICurrentService currentService, ProvinceQualityDao provinceMapper1) {
        this.areaService = areaService;
        this.objectMapper = objectMapper;
        this.type = objectMapper.getTypeFactory().constructCollectionType(List.class, StationQualityWeb.class);
        this.sqlSessionFactory = sqlSessionFactory;
        this.stationMapper = stationMapper;
        this.stationQualityMapper = stationQualityMapper;
        this.areaQualityMapper = areaQualityMapper;
        this.tableName = currentService.getCurrentDB();
        this.provinceMapper = provinceMapper1;
    }

    @PostConstruct
    private void init() {
        // 初始化所有市区列表
        log.info("初始化市区列表");
        LambdaQueryWrapper<Area> wrapper = new LambdaQueryWrapper<>();
        areaCodes = areaService.list(wrapper);
    }

    public List<Area> getAreaCodes() {
        return areaCodes;
    }

    //    @Async
    public void updateStationQuality(String cityName, AtomicBoolean flag) throws StationQualityExistException {
        if (!flag.get()) {
            log.info("取消更新");
            return;
        }
        SecurityContextHolder.setTableName(tableName);
        List<StationQualityWeb> webs = this.resolveJson(cityName);
        if (ObjectUtils.isEmpty(webs)) {
            return;
        }
        try {
            List<String> allCodes = webs.stream()
                    .peek(item -> {
                        item.setAQI(parse(item.getAQI()));
                        item.setPM2_5(parse(item.getPM2_5()));
                        item.setPM10(parse(item.getPM10()));
                        item.setSO2(parse(item.getSO2()));
                        item.setNO2(parse(item.getNO2()));
                        item.setCO(parse(item.getCO()));
                        item.setO3(parse(item.getO3()));
                        item.setPM2_5_24h(parse(item.getPM2_5_24h()));
                        item.setPM10_24h(parse(item.getPM10_24h()));
                        item.setSO2_24h(parse(item.getSO2_24h()));
                        item.setO3_8h(parse(item.getO3_8h()));
                    }).map(StationQualityWeb::getStationCode).toList(); //总的站点数据
            LambdaQueryWrapper<StationQuality> wrapper = new LambdaQueryWrapper<>();
            wrapper
                    .select(StationQuality::getStationCode)
                    .eq(StationQuality::getTimestamp, webs.get(0).getTimePoint())
                    .in(StationQuality::getStationCode, allCodes);
            List<String> existCodes = stationQualityMapper.selectObjs(wrapper); //已经存在的站点数据
            List<StationQuality> list = webs.stream().filter(stationQualityWeb -> !existCodes.contains(stationQualityWeb.getStationCode())).map(StationQualityWeb::toStationQuality).toList();
            if (ObjectUtils.isEmpty(list)) { //如果所有都存在则直接返回
                throw new StationQualityExistException("所有站点数据都已存在");
            }
            // 批量增加操作
            MybatisBatch.Method<StationQuality> mapperMethod = new MybatisBatch.Method<>(StationQualityMapper.class);
            MybatisBatch<StationQuality> batch = new MybatisBatch<>(sqlSessionFactory, list);
            batch.execute(mapperMethod.insert());
            log.info("成功将{}下监测站数据存入数据库", cityName);
        } catch (PersistenceException e) {
            log.error("{}监测站点数据存入数据库失败-->原因:{}", cityName, e.getMessage());
        }
        SecurityContextHolder.remove();
    }

    private String parse(String s) {
        if (StringUtils.isBlank(s)) {
            return abnormal; // 或者返回某个默认值，如Double.NaN
        }
        String[] split = s.split("<");
        String trim;
        if (split.length > 1) {
            trim = split[1];
        } else {
            trim = s;
        }
        try {
            Double.parseDouble(trim.trim());
            return trim;
        } catch (Exception e) {
            return abnormal;
        }
    }

    //    @Async
    public void updateStation(String cityName, Integer cityCode) {
        SecurityContextHolder.setTableName(tableName);
        List<StationQualityWeb> webs = this.resolveJson(cityName);
        if (ObjectUtils.isEmpty(webs)) {
            return;
        }
        try {
            //获取所有站点名称
            Set<String> stationList = webs.stream().map(StationQualityWeb::getStationCode).collect(Collectors.toSet());
            //站点信息
            List<Station> stations = webs.stream().map(stationQualityWeb -> stationQualityWeb.toStation(cityCode)).toList();
            LambdaQueryWrapper<Station> wrapper = new LambdaQueryWrapper<>();
            wrapper
                    .select(Station::getStationCode)
                    .eq(Station::getStatus, true)
                    .eq(Station::getCityCode, cityCode);
            //搜索当前已经存在的站点
            Set<String> list = new HashSet<>(stationMapper.selectObjs(wrapper));
            //获取当前市区下的站点数据
            Set<String> common = new HashSet<>(stationList);
            //获取交集 也就是都存在的站点
            common.retainAll(list);
            //获取需要删除的站点数据
            list.removeAll(common);
            //获取需要新增 或者修改的的站点数据
            stationList.removeAll(common);
            //获取变化的站点的code
            MybatisBatch.Method<Station> mapperMethod = new MybatisBatch.Method<>(StationMapper.class);
            if (ObjectUtils.isNotEmpty(stationList)) {
                List<Station> insertList = stations.stream().filter(station -> stationList.contains(station.getStationCode())).toList(); //需要新增的站点
                // 批量增加操作
                MybatisBatch<Station> batch = new MybatisBatch<>(sqlSessionFactory, insertList);
                batch.saveOrUpdate(mapperMethod.insert(), ((batchSqlSession, station) -> stationMapper.selectById(station.getStationCode()) == null), mapperMethod.updateById());
                log.info("成功在{}下新增站点:{}", cityName, insertList);
            }
            if (ObjectUtils.isNotEmpty(list)) {
                // 批量增加操作
                MybatisBatch<String> batch = new MybatisBatch<>(sqlSessionFactory, list.stream().toList());
                batch.execute(mapperMethod.update(e -> {
                    LambdaUpdateWrapper<Station> update = new LambdaUpdateWrapper<>();
                    update
                            .set(Station::getStatus, false)
                            .eq(Station::getStationCode, e);
                    return update;
                }));
                log.info("成功修改{}下站点:{}", cityName, list);
            }
        } catch (PersistenceException e) {
            log.error("{}监测站存入数据库失败-->原因:{}", cityName, e.getMessage());
        }
        SecurityContextHolder.remove();
    }

    //    @Async
    public void updateArea(Integer cityCode, List<Station> stations, LocalDateTime dateTime) {
        SecurityContextHolder.setTableName(tableName);
        List<String> list = stations.stream().map(Station::getStationCode).collect(Collectors.toList());
        //        获取站点下的空气质量数据
        LambdaQueryWrapper<StationQuality> wrapper = new LambdaQueryWrapper<>();
        wrapper
                .eq(StationQuality::getTimestamp, dateTime)
                .in(StationQuality::getStationCode, list);
        List<StationQuality> qualities = stationQualityMapper.selectList(wrapper);
        if (qualities.size() > 0) {
            AreaQuality areaQuality = new AreaQuality();
            areaQuality.setCityCode(cityCode);
            areaQuality.setTimestamp(dateTime);
            extracted(qualities, areaQuality);
            areaQualityMapper.insert(areaQuality);
        } else {
            SecurityContextHolder.remove();
            throw new Error("获取城市空气质量数据失败");
        }
    }

    public void updateProvinceQuality(Integer provinceId, List<Area> areaList, LocalDateTime time) {
        //获取市区下的所有 空气质量数据 然后进行平均
        LambdaQueryWrapper<AreaQuality> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(AreaQuality::getCityCode, areaList.stream().map(Area::getCityCode).toList())
                .eq(AreaQuality::getTimestamp, time);
        List<AreaQuality> qualities = areaQualityMapper.selectList(wrapper);
        if (ObjectUtils.isNotEmpty(qualities)) {
            ProvinceQuality provinceQuality = new ProvinceQuality();
            provinceQuality.setId(provinceId);
            provinceQuality.setTimestamp(time);
            extracted(qualities, provinceQuality);
            provinceMapper.insert(provinceQuality);
        }
    }

    private void extracted(List<? extends BaseQuality> qualities, BaseQuality baseQuality) {
        double pm25 = qualities.stream().map(BaseQuality::getPm25).filter(e -> !abnormal.equals(e)).collect(Collectors.summarizingDouble(Double::parseDouble)).getAverage();
        baseQuality.setPm25(String.format("%.2f", pm25));
        double pm10 = qualities.stream().map(BaseQuality::getPm10).filter(e -> !abnormal.equals(e)).collect(Collectors.summarizingDouble(Double::parseDouble)).getAverage();
        baseQuality.setPm10(String.format("%.2f", pm10));
        double so2 = qualities.stream().map(BaseQuality::getSo2).filter(e -> !abnormal.equals(e)).collect(Collectors.summarizingDouble(Double::parseDouble)).getAverage();
        baseQuality.setSo2(String.format("%.2f", so2));
        double no2 = qualities.stream().map(BaseQuality::getNo2).filter(e -> !abnormal.equals(e)).collect(Collectors.summarizingDouble(Double::parseDouble)).getAverage();
        baseQuality.setNo2(String.format("%.2f", no2));
        double co = qualities.stream().map(BaseQuality::getCo).filter(e -> !abnormal.equals(e)).collect(Collectors.summarizingDouble(Double::parseDouble)).getAverage();
        baseQuality.setCo(String.format("%.2f", co));
        double o3 = qualities.stream().map(BaseQuality::getO3).filter(e -> !abnormal.equals(e)).collect(Collectors.summarizingDouble(Double::parseDouble)).getAverage();
        baseQuality.setO3(String.format("%.2f", o3));
        double o3_8h = qualities.stream().map(BaseQuality::getO3H).filter(e -> !abnormal.equals(e)).collect(Collectors.summarizingDouble(Double::parseDouble)).getAverage();
        baseQuality.setO3H(String.format("%.2f", o3_8h));
        double pm25_24h = qualities.stream().map(BaseQuality::getPm25H).filter(e -> !abnormal.equals(e)).collect(Collectors.summarizingDouble(Double::parseDouble)).getAverage();
        baseQuality.setPm25H(String.format("%.2f", pm25_24h));
        double pm10_24h = qualities.stream().map(BaseQuality::getPm10H).filter(e -> !abnormal.equals(e)).collect(Collectors.summarizingDouble(Double::parseDouble)).getAverage();
        baseQuality.setPm10H(String.format("%.2f", pm10_24h));
        double so2_24h = qualities.stream().map(BaseQuality::getSo2H).filter(e -> !abnormal.equals(e)).collect(Collectors.summarizingDouble(Double::parseDouble)).getAverage();
        baseQuality.setSo2H(String.format("%.2f", so2_24h));
        new QualityUtils<>(baseQuality).exeAqi();
    }

    private List<StationQualityWeb> resolveJson(String cityName) {
        HttpRequest request = HttpUtil.createGet("https://air.cnemc.cn:18007/CityData/GetAQIDataPublishLive?cityName=" + cityName);
        try (HttpResponse response = request.execute()) {
            String body = response.body();
            return objectMapper.readValue(body, type);
        } catch (JsonProcessingException e) {
            log.error("{}市区列表解析失败", cityName);
            throw new JsonException();
        }
    }

    /**
     * 加个缓存对象
     *
     * @param key      对象类的表名
     * @param division 策略
     */
    @SneakyThrows
    public void setStrategy(String key, Class<? extends BaseDivisionFactory> division) {
        String className = division.getSimpleName();
        BaseDivisionFactory fac = getFactory(className);
        if (ObjectUtils.isEmpty(fac)) {
            fac = division.getConstructor().newInstance();
            setFactory(className, fac);
        }
        strategy.put(key, fac);
    }

    public String getTableName() {
        return tableName;
    }

    public void setTableName(String tableName) {
        this.tableName = tableName;
    }

    public static DivisionInterface getStrategy(String key) {
        return strategy.get(key);
    }

    public <T extends BaseDivisionFactory> void setFactory(String key, T value) {
        factory.put(key, value);
    }

    public BaseDivisionFactory getFactory(String key) {
        return factory.get(key);
    }
}
